/*****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations
 * under the License.
 ****************************************************************/
package reco.db.field;

import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;

import reco.db.Constraint;
import reco.db.ConstraintFactory;
import reco.db.Domain;

/**
 * Fields which have a type defined as a sequence and make sense to be filtered on "ranges" (as integers or dates).
 * 
 * @param <T>
 *            field's type.
 */
@SuppressWarnings("serial")
public abstract class ComparableValueField<T, D extends Domain<D>> extends ValueField<T, D> {

    /**
     * Creates a field defining only its type, and leaving its definition open.
     * <p>
     * After the field construction is done, the client <em>must</em> define the field's name and domain, and <em>can</em> add constraints to the
     * field.
     * 
     * @param fieldType
     *            Type of field's possible values.
     * @param fieldConstraintsDefinitions
     *            Definitions of {@link Constraint}'s to be atacched to the created field during construction.
     * @throws NullPointerException
     *             if <code>fieldType</code> is <code>null</code>.
     * 
     * @see #setName(String)
     * @see #setDomain(Class)
     */
    public ComparableValueField(Class<T> fieldType, ConstraintFactory... fieldConstraintsDefinitions) {
        super(fieldType, fieldConstraintsDefinitions);
    }

    /**
     * Creates a field defining its type, name and domain, and leaving its definition open.
     * <p>
     * After the field construction is done, the client <em>can</em> define the field's name and domain, and <em>can</em> add constraints to the
     * field.
     * 
     * @param fieldType
     *            Type of field's possible values.
     * @param fieldName
     *            Name used to recognize the field in the domain.
     * @param fieldDomain
     *            Domain where the field is defined.
     * @param fieldConstraintsDefinitions
     *            Definitions of {@link Constraint}'s to be atacched to the created field during construction.
     * @throws NullPointerException
     *             if <code>fieldType</code> is <code>null</code>.
     * @throws NullPointerException
     *             if <code>fieldName</code> is <code>null</code>.
     * @throws NullPointerException
     *             if <code>fieldDomain</code> is <code>null</code>.
     */
    public ComparableValueField(Class<T> fieldType, String fieldName, Class<D> fieldDomain, ConstraintFactory... fieldConstraintsDefinitions) {
        super(fieldType, fieldName, fieldDomain, fieldConstraintsDefinitions);
    }

    /**
     * Creates a filter for domains which has this field with a value in the defined range.
     * 
     * @param start
     *            Start of the range, the filter <em>include</em> this value.
     * @param end
     *            End of the range, the filter <em>include</em> this value.
     * @return a filter of domains with this value greater or equal to <code>start</code> and lower or equal to <code>end</code>.
     */
    public Expression between(T start, T end) {
        return ExpressionFactory.betweenExp(getName(), start, end);
    }

    /**
     * Creates a filter for domains which has this field with a value out of the defined range.
     * 
     * @param start
     *            Start of the range, the filter <em>include</em> this value.
     * @param end
     *            End of the range, the filter <em>include</em> this value.
     * @return a filter of domains with this value lower to <code>start</code> and greater than <code>end</code>.
     */
    public Expression notBetween(T start, T end) {
        return ExpressionFactory.notBetweenExp(getName(), start, end);
    }

    /**
     * Creates a filter for domains which has this field lower than a value.
     * 
     * @param value
     *            Value against which the field must be compared, if the field's value is greater or equal than <code>value</code>, then the domain
     *            would not be considered.
     * @return a filter of domains with this field lower than <code>value</code>.
     */
    public Expression lt(T value) {
        return ExpressionFactory.lessExp(getName(), value);
    }

    /**
     * Creates a filter for domains which has this field greater than a value.
     * 
     * @param value
     *            Value against which the field must be compared, if the field's value is lower or equal than <code>value</code>, then the domain
     *            would not be considered.
     * @return a filter of domains with this field greater than <code>value</code>.
     */
    public Expression gt(T value) {
        return ExpressionFactory.greaterExp(getName(), value);
    }

    /**
     * Creates a filter for domains which has this field lower or equal to a value.
     * 
     * @param value
     *            Value against which the field must be compared, if the field's value is greater than <code>value</code>, then the domain would not
     *            be considered.
     * @return a filter of domains with this field lower or equal to <code>value</code>.
     */
    public Expression le(T value) {
        return ExpressionFactory.lessOrEqualExp(getName(), value);
    }

    /**
     * Creates a filter for domains which has this field greater or equal to a value.
     * 
     * @param value
     *            Value against which the field must be compared, if the field's value is lower than <code>value</code>, then the domain would not be
     *            considered.
     * @return a filter of domains with this field greater or equal to <code>value</code>.
     */
    public Expression ge(T value) {
        return ExpressionFactory.greaterOrEqualExp(getName(), value);
    }
}
